use crate::commands::command::build_command;
use crate::commands::common::ErrMsg;
use crate::commands::redis::ContentType::ERROR;
use crate::commands::{
    redis::{RedisRequest, RedisResponse},
    redis_grpc::Redis,
};
use futures::{FutureExt, TryFutureExt};
use grpcio::{RpcContext, UnarySink};

#[derive(Clone, Default)]
pub struct RedisService {}

impl Redis for RedisService {
    fn exec(&mut self, ctx: RpcContext, req: RedisRequest, sink: UnarySink<RedisResponse>) {
        println!("--> request {:?}", req);

        let response = match build_command(&req) {
            Err(e) => build_err_resp(&e),
            Ok(mut cmd) => {
                if let Err(e) = cmd.parse(&req) {
                    build_err_resp(&e)
                } else {
                    cmd.execute().unwrap_or_else(|e| build_err_resp(&e))
                }
            }
        };

        let f = sink
            .success(response)
            .map_err(move |err| eprintln!("Failed to reply: {:?}", err))
            .map(|reply| println!("Responded with {{ {:?} }}", reply));
        ctx.spawn(f);
    }
}

fn build_err_resp(err: &ErrMsg) -> RedisResponse {
    let mut resp = RedisResponse::new();
    resp.set_field_type(ERROR);
    resp.set_message(err.msg().as_bytes().to_vec());
    resp
}
